{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Triage False Positive Findings\n",
    "\n",
    "This notebook demonstrates how to identify and mark false positive findings, specifically focusing on noseyparker_secret findings with generic password detections.\n",
    "\n",
    "## Setup\n",
    "\n",
    "First, let's import the required libraries and set up our connection to Hasura."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import json\n",
    "import os\n",
    "import re\n",
    "\n",
    "from gql import Client, gql\n",
    "from gql.transport.requests import RequestsHTTPTransport\n",
    "\n",
    "# Configuration - Set your username here\n",
    "username = \"analyst\"  # Change this to your username\n",
    "\n",
    "# Set up the GraphQL client\n",
    "hasura_url = os.getenv(\"HASURA_GRAPHQL_URL\", \"http://hasura:8080/v1/graphql\")\n",
    "admin_secret = os.getenv(\"HASURA_ADMIN_SECRET\", \"\")\n",
    "\n",
    "transport = RequestsHTTPTransport(url=hasura_url, headers={\"x-hasura-admin-secret\": admin_secret})\n",
    "\n",
    "client = Client(transport=transport, fetch_schema_from_transport=True)\n",
    "print(f\"Connected to Hasura at: {hasura_url}\")\n",
    "print(f\"Username set to: {username}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Query noseyparker_secret Findings\n",
    "\n",
    "Let's first pull all findings with finding_name = \"noseyparker_secret\" and examine their structure."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Query for noseyparker_secret findings\n",
    "query = gql(\"\"\"\n",
    "    query {\n",
    "        findings(where: {finding_name: {_eq: \"noseyparker_secret\"}}) {\n",
    "            finding_id\n",
    "            finding_name\n",
    "            category\n",
    "            severity\n",
    "            object_id\n",
    "            origin_type\n",
    "            origin_name\n",
    "            raw_data\n",
    "            data\n",
    "            created_at\n",
    "            triage_id\n",
    "        }\n",
    "    }\n",
    "\"\"\")\n",
    "\n",
    "result = client.execute(query)\n",
    "noseyparker_findings = result[\"findings\"]\n",
    "\n",
    "print(f\"Found {len(noseyparker_findings)} noseyparker_secret findings\")\n",
    "\n",
    "# Display first finding as example\n",
    "if noseyparker_findings:\n",
    "    print(\"\\n=== Example Finding ===\")\n",
    "    example_finding = noseyparker_findings[0]\n",
    "    for key, value in example_finding.items():\n",
    "        if key in [\"raw_data\", \"data\"]:\n",
    "            print(f\"{key}: {json.dumps(value, indent=2)}\")\n",
    "        else:\n",
    "            print(f\"{key}: {value}\")\n",
    "else:\n",
    "    print(\"No noseyparker_secret findings found\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Filter for Generic Password Rules\n",
    "\n",
    "Now let's filter findings where the raw_data.match.rule_name equals \"Generic Password\"."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Filter findings with Generic Password rule\n",
    "generic_password_findings = []\n",
    "\n",
    "for finding in noseyparker_findings:\n",
    "    raw_data = finding.get(\"raw_data\", {})\n",
    "    match_data = raw_data.get(\"match\", {})\n",
    "    rule_name = match_data.get(\"rule_name\", \"\")\n",
    "\n",
    "    if rule_name == \"Generic Password\":\n",
    "        generic_password_findings.append(finding)\n",
    "\n",
    "print(f\"Found {len(generic_password_findings)} findings with 'Generic Password' rule\")\n",
    "\n",
    "# Show some examples\n",
    "for i, finding in enumerate(generic_password_findings[:3]):\n",
    "    print(f\"\\n=== Generic Password Finding {i + 1} ===\")\n",
    "    print(f\"Finding ID: {finding['finding_id']}\")\n",
    "    print(f\"Rule Name: {finding['raw_data']['match']['rule_name']}\")\n",
    "    print(f\"Matched Content: {finding['raw_data']['match'].get('matched_content', 'N/A')}\")\n",
    "    print(f\"Snippet: {finding['raw_data']['match'].get('snippet', 'N/A')[:100]}...\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Mark Generic Password Findings as False Positive\n",
    "\n",
    "Let's mark all Generic Password findings as false positives by inserting records into the findings_triage_history table."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Mark Generic Password findings as false positive\n",
    "if generic_password_findings:\n",
    "    # Prepare triage history entries\n",
    "    triage_entries = []\n",
    "    for finding in generic_password_findings:\n",
    "        triage_entries.append(\n",
    "            {\"finding_id\": finding[\"finding_id\"], \"username\": username, \"automated\": True, \"value\": \"false_positive\"}\n",
    "        )\n",
    "\n",
    "    # Insert triage entries\n",
    "    mutation = gql(\"\"\"\n",
    "        mutation InsertTriageEntries($objects: [findings_triage_history_insert_input!]!) {\n",
    "            insert_findings_triage_history(objects: $objects) {\n",
    "                returning {\n",
    "                    id\n",
    "                    finding_id\n",
    "                    username\n",
    "                    value\n",
    "                    timestamp\n",
    "                }\n",
    "            }\n",
    "        }\n",
    "    \"\"\")\n",
    "\n",
    "    result = client.execute(mutation, variable_values={\"objects\": triage_entries})\n",
    "    inserted_entries = result[\"insert_findings_triage_history\"][\"returning\"]\n",
    "\n",
    "    print(f\"Successfully marked {len(inserted_entries)} Generic Password findings as false positive\")\n",
    "\n",
    "    # Show some examples\n",
    "    for entry in inserted_entries[:3]:\n",
    "        print(f\"  - Finding ID {entry['finding_id']}: {entry['value']} by {entry['username']} at {entry['timestamp']}\")\n",
    "else:\n",
    "    print(\"No Generic Password findings to mark as false positive\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Filter for 'password123' Pattern\n",
    "\n",
    "Now let's find findings where the matched_content matches a pattern for 'password123' and mark them as false positives."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Filter findings with 'password123' pattern\n",
    "password123_pattern = re.compile(r\"password123\", re.IGNORECASE)\n",
    "password123_findings = []\n",
    "\n",
    "for finding in noseyparker_findings:\n",
    "    raw_data = finding.get(\"raw_data\", {})\n",
    "    match_data = raw_data.get(\"match\", {})\n",
    "    matched_content = match_data.get(\"matched_content\", \"\")\n",
    "\n",
    "    if password123_pattern.search(matched_content):\n",
    "        password123_findings.append(finding)\n",
    "\n",
    "print(f\"Found {len(password123_findings)} findings with 'password123' pattern\")\n",
    "\n",
    "# Show examples\n",
    "for i, finding in enumerate(password123_findings[:3]):\n",
    "    print(f\"\\n=== Password123 Finding {i + 1} ===\")\n",
    "    print(f\"Finding ID: {finding['finding_id']}\")\n",
    "    print(f\"Rule Name: {finding['raw_data']['match'].get('rule_name', 'N/A')}\")\n",
    "    print(f\"Matched Content: {finding['raw_data']['match'].get('matched_content', 'N/A')}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Mark password123 Findings as False Positive\n",
    "\n",
    "Let's mark all findings containing 'password123' as false positives."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Mark password123 findings as false positive\n",
    "if password123_findings:\n",
    "    # Prepare triage history entries\n",
    "    triage_entries = []\n",
    "    for finding in password123_findings:\n",
    "        triage_entries.append(\n",
    "            {\"finding_id\": finding[\"finding_id\"], \"username\": username, \"automated\": True, \"value\": \"false_positive\"}\n",
    "        )\n",
    "\n",
    "    # Insert triage entries\n",
    "    mutation = gql(\"\"\"\n",
    "        mutation InsertTriageEntries($objects: [findings_triage_history_insert_input!]!) {\n",
    "            insert_findings_triage_history(objects: $objects) {\n",
    "                returning {\n",
    "                    id\n",
    "                    finding_id\n",
    "                    username\n",
    "                    value\n",
    "                    timestamp\n",
    "                }\n",
    "            }\n",
    "        }\n",
    "    \"\"\")\n",
    "\n",
    "    result = client.execute(mutation, variable_values={\"objects\": triage_entries})\n",
    "    inserted_entries = result[\"insert_findings_triage_history\"][\"returning\"]\n",
    "\n",
    "    print(f\"Successfully marked {len(inserted_entries)} password123 findings as false positive\")\n",
    "\n",
    "    # Show some examples\n",
    "    for entry in inserted_entries[:3]:\n",
    "        print(f\"  - Finding ID {entry['finding_id']}: {entry['value']} by {entry['username']} at {entry['timestamp']}\")\n",
    "else:\n",
    "    print(\"No password123 findings to mark as false positive\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
